﻿#include "stdafx.h"
#include "main_data_c.h"
#include "ui/msgbox.h"
#include "utils/Debug.h"
#include "GameNet.h"
#include "ClientProtocol.h"

#define HANDLE_(x) case x: handle_##x(data); break;

extern "C"
{
    void sub_407620(BYTE*);
    void sub_407680(BYTE*);
    void sub_407710(BYTE*);
    void sub_40AD00(BYTE*);
    void sub_40BFA0(BYTE*);
    void sub_40AF40(BYTE*);
    void sub_408470(BYTE*);
    void sub_40A070(BYTE*);
    void sub_409AE0(BYTE*);
    void sub_408750(BYTE*);
    void sub_407F60(BYTE*);
    void sub_40AEC0(BYTE*);
    void sub_40A950(BYTE*);
    void sub_40AF70(BYTE*);
    void sub_40AFC0(BYTE*);
    void sub_40B110(BYTE*);
    void sub_40B1B0(BYTE*);
    void sub_40B200(BYTE*);
    void sub_408DD0(BYTE*);
    void sub_40B620(BYTE*);
    void sub_40B980(BYTE*);
    void sub_40B9D0(BYTE*);
    void sub_40B8E0(BYTE*);
    void sub_40B930(BYTE*);
    void sub_40BB20(BYTE*);
    void sub_40BC00(BYTE*);
    void sub_40BDD0(BYTE*);
    void sub_40B2C0(BYTE*);
    void sub_40C020(BYTE*);
}

void ClientProtocol::initEncDec(const char* encPath, const char* decPath)
{
    if (!m_encDec.ReadDec_dat(decPath)) {
        MsgNoFile_exit(decPath, NULL);
        return;
    }

    if (!m_encDec.ReadEnc_dat(encPath)) {
        MsgNoFile_exit(encPath, NULL);
    }

}
void ClientProtocol::handlePacket(BYTE* data)
{
    int len;
    static BYTE decBuf[0x2000];
    bool needDec = false;

    switch (*data)
    {
    case 0xc1:
        len = data[1] - 2;
        data += 2;
        break;
    case 0xc2:
        len = (data[1] << 8) + data[2] - 3;
        data += 3;
        break;
    case 0xc3:
        len = data[1] - 2;
        data += 2;
        needDec = true;
        break;
    case 0xc4:
        len = (data[1] << 8) + data[2] - 3;
        data += 3;
        needDec = true;
        break;
    default:
        return;
    }
    if (needDec) {
        len = m_encDec.DecodeC3C4(decBuf + 3, data, len - 3);
        if (!len) return;
        if (len > 0xff - 2) {
            len += 3;
            decBuf[0] = 0xc2;
            decBuf[1] = len >> 8;
            decBuf[2] = len;
        }
        else {
            decBuf[1] = 0xc1;
            decBuf[2] = len + 2;
        }
        data = decBuf + 3;
    }

    switch (*data)
    {
        HANDLE_(0x00);
        HANDLE_(0x01);
        HANDLE_(0x02);
        HANDLE_(0x03);
        HANDLE_(0x0B);
        HANDLE_(0x0C);
        HANDLE_(0x0D);
        HANDLE_(0x0F);
        HANDLE_(0x10);
        HANDLE_(0x11);
        HANDLE_(0x12);
        HANDLE_(0x13);
        HANDLE_(0x14);
        HANDLE_(0x15);
        HANDLE_(0x16);
        HANDLE_(0x17);
        HANDLE_(0x18);
        HANDLE_(0x19);
        HANDLE_(0x1A);
        HANDLE_(0x1B);
        HANDLE_(0x1C);
        HANDLE_(0x1E);
        HANDLE_(0x1F);
        HANDLE_(0x20);
        HANDLE_(0x21);
        HANDLE_(0x22);
        HANDLE_(0x23);
        HANDLE_(0x24);
        HANDLE_(0x25);
        HANDLE_(0x26);
        HANDLE_(0x27);
        HANDLE_(0x28);
        HANDLE_(0x29);
        HANDLE_(0x2A);
        HANDLE_(0x30);
        HANDLE_(0x31);
        HANDLE_(0x32);
        HANDLE_(0x33);
        HANDLE_(0x36);
        HANDLE_(0x37);
        HANDLE_(0x38);
        HANDLE_(0x39);
        HANDLE_(0x3A);
        HANDLE_(0x3B);
        HANDLE_(0x3C);
        HANDLE_(0x3D);
        HANDLE_(0x40);
        HANDLE_(0x41);
        HANDLE_(0x42);
        HANDLE_(0x43);
        HANDLE_(0x45);
        HANDLE_(0x50);
        HANDLE_(0x51);
        HANDLE_(0x52);
        HANDLE_(0x53);
        HANDLE_(0x54);
        HANDLE_(0x55);
        HANDLE_(0x56);
        HANDLE_(0x5A);
        HANDLE_(0x5B);
        HANDLE_(0x5C);
        HANDLE_(0x5D);
        HANDLE_(0x60);
        HANDLE_(0x61);
        HANDLE_(0x62);
        HANDLE_(0x63);
        HANDLE_(0x64);
        HANDLE_(0x81);
        HANDLE_(0x82);
        HANDLE_(0x86);
        HANDLE_(0x87);
        HANDLE_(0xF1);
        HANDLE_(0xF3);
        HANDLE_(0xF4);
    default:
        break;
    }
}

void ClientProtocol::handle_0x00(BYTE* data)
{
    if (nScene_482904 == 2) {
        // 连接上服务器
        // 请求服务器列表
        SendC1F402_405980();
    }
    else {
        // 文字消息
        onPlayerMessage((PMSG_PlayerMessage*)(data + 1));
    }
}

void ClientProtocol::handle_0x01(BYTE* data)
{
    onNpcMessage((PMSG_NpcMessage*)(data + 1));
}

void ClientProtocol::handle_0x02(BYTE* data)
{
    onPlayerPrivateMessage((PMSG_PlayerMessage*)(data + 1));
}

void ClientProtocol::handle_0x03(BYTE* data)
{
    Send_03_405AB0(0x12345678);
}

void ClientProtocol::handle_0x0B(BYTE* data)
{
    sub_40AF40(data - 2);
}

void ClientProtocol::handle_0x0C(BYTE* data)
{
    if (!data[1]) {
        gamePrintLog(mainData.name_57273B8, mainData.textBmd_7B7BCE8[482], 2);
    }
}

void ClientProtocol::handle_0x0D(BYTE* data)
{
    onSystemNotice((PMSG_SystemNotice*)(data + 1));
}

void ClientProtocol::handle_0x0F(BYTE* data)
{
    BYTE heartbeat = data[1];
    BYTE n = heartbeat >> 4;
    if (n == 0) {
        mainData.dword_7B785B0 = 0;
    }
    else if (n == 1) {
        mainData.dword_7B785B0 = (heartbeat & 0xf) * 6;
    }
}

void ClientProtocol::handle_0x10(BYTE* data)
{
    onGameObjMove((PMSG_GameObjMove*)(data + 1));
}

void ClientProtocol::handle_0x11(BYTE* data)
{
    sub_408470(data - 2);
}

void ClientProtocol::handle_0x12(BYTE* data)
{
    onMeetThePlayer((PMSG_MeetThePlayer*)(data + 1));
}

void ClientProtocol::handle_0x13(BYTE* data)
{
    onMeetTheMonster((PMSG_MeetTheMonster*)(data + 1));
}

void ClientProtocol::handle_0x14(BYTE* data)
{
    onGameObjOutOfRange((PMSG_GameObjOutOfRange*)(data + 1));
}

void ClientProtocol::handle_0x15(BYTE* data)
{
    onBloodLoss((PMSG_BloodLoss*)(data + 1));
}

void ClientProtocol::handle_0x16(BYTE* data)
{
    //sub_40A1D0(data - 2);
    onKillMonster((PMSG_KillMonster*)(data + 1));
}

void ClientProtocol::handle_0x17(BYTE* data)
{
    //sub_40A2F0(data - 2);
    onGameObjDeath(*(WORD*)(data + 1));
}

void ClientProtocol::handle_0x18(BYTE* data)
{
    onGameObjAction((PMSG_GameObjAction*)(data + 1));
}

void ClientProtocol::handle_0x19(BYTE* data)
{
    onUseSingleSkill((PMSG_UseSingleSkill*)(data + 1));
}

void ClientProtocol::handle_0x1A(BYTE* data)
{
    sub_40A070(data - 2);
}

void ClientProtocol::handle_0x1B(BYTE* data)
{
    sub_409AE0(data - 2);
}

void ClientProtocol::handle_0x1C(BYTE* data)
{
    onEnterTheGate((PMSG_EnterTheGate*)(data + 1));
}

void ClientProtocol::handle_0x1E(BYTE* data)
{
    onUseRangeSkill((PMSG_UseRangeSkill*)(data + 1));
}

void ClientProtocol::handle_0x1F(BYTE* data)
{
    onMeetTheSummon((PMSG_MeetTheSummon*)(data + 1));
}

void ClientProtocol::handle_0x20(BYTE* data)
{
    onItemsOnGround((PMSG_ItemsOnGround*)(data + 1));
}

void ClientProtocol::handle_0x21(BYTE* data)
{
    onItemsLostOnGround((PMSG_ItemsLostOnGround*)(data + 1));
}

void ClientProtocol::handle_0x22(BYTE* data)
{
    onAddItemToBag((PMSG_GridItem*)(data + 1));
}

void ClientProtocol::handle_0x23(BYTE* data)
{
    onDiscardItem((PMSG_DiscardItem*)(data + 1));
}

void ClientProtocol::handle_0x24(BYTE* data)
{
    onMoveItem((PMSG_MoveItem*)(data + 1));
}

void ClientProtocol::handle_0x25(BYTE* data)
{
    sub_408750(data - 2);
}

void ClientProtocol::handle_0x26(BYTE* data)
{
    onLifeChange((PMSG_LifeManaChange*)(data + 1));
}

void ClientProtocol::handle_0x27(BYTE* data)
{
    onManaChange((PMSG_LifeManaChange*)(data + 1));
}

void ClientProtocol::handle_0x28(BYTE* data)
{
    sub_407F60(data - 2);
}

void ClientProtocol::handle_0x29(BYTE* data)
{
    sub_40AEC0(data - 2);
}

void ClientProtocol::handle_0x2A(BYTE* data)
{
    onUsePotion((PMSG_UsePotion*)(data + 1));
}

void ClientProtocol::handle_0x30(BYTE* data)
{
    onOpenBag(data[1]);
}

void ClientProtocol::handle_0x31(BYTE* data)
{
    onShopItems((PMSG_ShopItems*)(data + 1));
}

void ClientProtocol::handle_0x32(BYTE* data)
{
    sub_40A950(data - 2);
}

void ClientProtocol::handle_0x33(BYTE* data)
{
    onSellItem((PMSG_SellItem*)(data + 1));
}

void ClientProtocol::handle_0x36(BYTE* data)
{
    sub_40AF70(data - 2);
}

void ClientProtocol::handle_0x37(BYTE* data)
{
    sub_40AFC0(data - 2);
}

void ClientProtocol::handle_0x38(BYTE* data)
{
    sub_40B110(data - 2);
}

void ClientProtocol::handle_0x39(BYTE* data)
{
    sub_433E00(mainData.word_7CC3B00, 8, 4, data[1], data[2], data[3], data[4], 1);
    sub_401570(29, 0, 0);
}

void ClientProtocol::handle_0x3A(BYTE* data)
{
    mainData.dword_7CC88A8 = data[1] != 0 ? mainData.dword_57273A4 : 0;
}

void ClientProtocol::handle_0x3B(BYTE* data)
{
    // 数据长度不明，不知道偏移 data1 or data2
    mainData.dword_7CC88A4 = data[1];
}

void ClientProtocol::handle_0x3C(BYTE* data)
{
    sub_40B1B0(data - 2);
}

void ClientProtocol::handle_0x3D(BYTE* data)
{
    sub_40B200(data - 2);
}

void ClientProtocol::handle_0x40(BYTE* data)
{
    mainData.dword_81C498C = 120;
    mainData.dword_7CC88A0 = ntohs(*(WORD*)(data + 1));
}

void ClientProtocol::handle_0x41(BYTE* data)
{
    char* msg = nullptr;
    switch (data[1])
    {
    case 0: // 组队失败
        msg = mainData.textBmd_7B7BCE8[497];
        break;
    case 1: // 对方拒绝了组队的要求
        msg = mainData.textBmd_7B7BCE8[498];
        break;
    case 2: // 对方队伍的队员太多了
        msg = mainData.textBmd_7B7BCE8[499];
        break;
    case 3: // 对方中断连接了
        msg = mainData.textBmd_7B7BCE8[500];
        break;
    case 4: // 对方加入别的队伍了
        msg = mainData.textBmd_7B7BCE8[501];
        break;
    default:
        break;
    }
    if (msg) {
        gamePrintLog("", msg, 2);
    }
    mainData.dword_81C498C = 0;
}

void ClientProtocol::handle_0x42(BYTE* data)
{
    onTeamInfo((PMSG_TeamInfo*)(data + 1));
}

void ClientProtocol::handle_0x43(BYTE* data)
{
    mainData.dword_7CC889C = 0;
    // 离开队伍了
    gamePrintLog("", mainData.textBmd_7B7BCE8[502], 2);
}

void ClientProtocol::handle_0x45(BYTE* data)
{
    // 数据长度不明，不知道偏移
    sub_408DD0(data - 3);
}

void ClientProtocol::handle_0x50(BYTE* data)
{
    mainData.dword_81C498C = 119;
    mainData.dword_7CC8894 = ntohs(*(WORD*)(data + 1));
}

void ClientProtocol::handle_0x51(BYTE* data)
{
    char* msg = nullptr;
    switch (data[1])
    {
    case 0: // 战盟盟主拒绝你加入他的战盟
        msg = mainData.textBmd_7B7BCE8[503];
        break;
    case 1: // 加入战盟了
        msg = mainData.textBmd_7B7BCE8[504];
        break;
    case 2: // 超过战盟最大的人数限制了
        msg = mainData.textBmd_7B7BCE8[505];
        break;
    case 3: // 对方中断连接了
        msg = mainData.textBmd_7B7BCE8[506];
        break;
    case 4: // 对方不是战盟盟主
        msg = mainData.textBmd_7B7BCE8[507];
        break;
    case 5: // 无法重复加入
        msg = mainData.textBmd_7B7BCE8[508];
        break;
    case 6: // 无法提出加入申请的状态
        msg = mainData.textBmd_7B7BCE8[509];
        break;
    case 7: // 要加入战盟等级必须最少要 6级以上
        msg = mainData.textBmd_7B7BCE8[510];
        break;
    default:
        break;
    }
    if (msg)
        gamePrintLog("", msg, 2);
    mainData.dword_81C498C = 0;
}

void ClientProtocol::handle_0x52(BYTE* data)
{
    onGuildInfo((PMSG_GuildInfo*)(data + 1));
}

void ClientProtocol::handle_0x53(BYTE* data)
{
    sub_40B620(data - 2);
}

void ClientProtocol::handle_0x54(BYTE* data)
{
    mainData.byte_7CC88C4 = 1;
    mainData.btnGuild_7CC88BC = 0;
    mainData.btnTeam_7CC88BD = 0;
    mainData.btnCharacter_7CC88BE = 0;
    mainData.btnItem_7CC88BF = 0;
    mainData.byte_7CC88C0 = 0;
    mainData.byte_7CC88C1 = 0;
    mainData.byte_7CC88C3 = 0;
    mainData.dword_7CC88CC = 0;
}

void ClientProtocol::handle_0x55(BYTE* data)
{
    byte_47CEB8 = 0;
    mainData.byte_7CC88C4 = 1;
    mainData.dword_7CC88CC = 1;
    mainData.byte_7C41CEF = 1;
    sub_426F20();
    strBufSizeLimit_47CEC8[0] = 8;
    dword_47CEBC = 0;
    *(WORD*)(mainData.dword_79DBE88 + 404) = 999;
}

void ClientProtocol::handle_0x56(BYTE* data)
{
    switch (data[1])
    {
    case 0: // 这个战盟名称已经有人使用了
        gamePrintLog("", mainData.textBmd_7B7BCE8[516], 2);
        break;
    case 1:
        memset(mainData.strBuf_7BE8BC0, 0, 10);
        mainData.strBufDataSize_7BA8558[0] = 0;
        strBufSizeLimit_47CEC8[0] = 10;
        mainData.byte_7C41CEF = 0;
        mainData.byte_7CC88C4 = 0;
        mainData.dword_7CC88CC = 0;
        sub_401570(28, 0, 0);
        mainData.dword_7C41CB8 = 0;
        dword_47CE38 = 6;
        break;
    case 2: // 战盟名称最少需要 2个以上汉字或4个以上字母
        gamePrintLog("", mainData.textBmd_7B7BCE8[517], 2);
        break;
    case 3: // 已经加入战盟了
        gamePrintLog("", mainData.textBmd_7B7BCE8[518], 2);
        break;
    default:
        break;
    }
}

void ClientProtocol::handle_0x5A(BYTE* data)
{
    sub_40B980(data - 3);
}

void ClientProtocol::handle_0x5B(BYTE* data)
{
    sub_40B9D0(data - 3);
}

void ClientProtocol::handle_0x5C(BYTE* data)
{
    sub_40B8E0(data - 2);
}

void ClientProtocol::handle_0x5D(BYTE* data)
{
    sub_40B930(data - 2);
}

void ClientProtocol::handle_0x60(BYTE* data)
{
    switch (data[1])
    {
    case 0: // 这个战盟不存在
        gamePrintLog("", mainData.textBmd_7B7BCE8[519], 2);
        break;
    case 1: // 向对方战盟宣战了
        gamePrintLog("", mainData.textBmd_7B7BCE8[520], 2);
        break;
    case 2: // 对方战盟盟主未上线
        gamePrintLog("", mainData.textBmd_7B7BCE8[521], 2);
        break;
    case 3: // 没有这个战盟
        gamePrintLog("", mainData.textBmd_7B7BCE8[522], 2);
        break;
    case 4: // 无法宣战的状态
        gamePrintLog("", mainData.textBmd_7B7BCE8[523], 2);
        break;
    case 5: // 只有战盟盟主可以宣战
        gamePrintLog("", mainData.textBmd_7B7BCE8[524], 2);
        break;
    case 6: // 对方战盟盟主拒绝你的宣战
        gamePrintLog("", mainData.textBmd_7B7BCE8[525], 2);
        break;
    default:
        break;
    }
    if (data[1] != 1)
        sub_407260();
}

void ClientProtocol::handle_0x61(BYTE* data)
{
    sub_40BB20(data - 2);
}

void ClientProtocol::handle_0x62(BYTE* data)
{
    sub_40BC00(data - 2);
}

void ClientProtocol::handle_0x63(BYTE* data)
{
    sub_40BDD0(data - 2);
}

void ClientProtocol::handle_0x64(BYTE* data)
{
    mainData.byte_572742C = 1;
    mainData.dword_57273B0 = data[1];
    mainData.dword_57273B4 = data[2];
}

void ClientProtocol::handle_0x81(BYTE* data)
{
    sub_40B2C0(data - 2);
}

void ClientProtocol::handle_0x82(BYTE* data)
{
    mainData.btnItem_7CC88BF = 0;
    sub_433BE0();
    mainData.dword_7CAFFB8 = 0;
    if (mainData.dword_81C498C == 116)
    {
        mainData.dword_81C498C = 0;
        sub_426F20();
        strBufSizeLimit_47CEC8[0] = 42;
        dword_47CEBC = 2;
        mainData.byte_7C41CF1 = 0;
        byte_47CEB8 = 0;
    }

    if (mainData.isDebug_81C4978)
        gamePrintLog("", "0x82 recv", 1);
}

void ClientProtocol::handle_0x86(BYTE* data)
{
    sub_40C020(data - 2);
}

void ClientProtocol::handle_0x87(BYTE* data)
{
    mainData.btnItem_7CC88BF = 0;
    sub_433BE0();
    mainData.dword_7CAFFB8 = 0;
    if (mainData.dword_81C498C == 116)
    {
        mainData.dword_81C498C = 0;
        sub_426F20();
        strBufSizeLimit_47CEC8[0] = 42;
        dword_47CEBC = 2;
        mainData.byte_7C41CF1 = 0;
        byte_47CEB8 = 0;
    }

    if (mainData.isDebug_81C4978)
        gamePrintLog("", "0x87 recv", 1);
}

void ClientProtocol::handle_0xF1(BYTE* data)
{
    switch (data[1])
    {
    case 0:
        onConnectedGameServer((PMSG_GameObjIdAndVersion*)(data + 2));
        break;
    case 1:
        onLoginResult(data[2]);
        break;
    case 2:
        onSceneSelected(data[2]);
        break;
    case 3:
        sub_407620(data - 2);
        break;
    case 4:
        sub_407680(data - 2);
        break;
    case 5:
        sub_407710(data - 2);
        break;
    case 0x12:
        if (data[2])
        {
            if (data[2] == 1)
            {
                mainData.dword_57273C8 = 11;
            }
            else if (data[2] == 2)
            {
                mainData.dword_57273C8 = 13;
            }
        }
        else
        {
            mainData.dword_57273C8 = 12;
        }
        break;
    default:
        assert(false);
        break;
    }
}

void ClientProtocol::handle_0xF3(BYTE* data)
{
    switch (data[1])
    {
    case 0:
        onCharacterList((PMSG_CharacterList*)(data + 2));
        break;
    case 1:
        onCreateCharacter((PMSG_CreateCharacter_result*)(data + 2));
        break;
    case 2:
        onCharacterDel(data[2]);
        break;
    case 3:
        onCharacterInfo((PMSG_CharacterInfo*)(data + 2));
        break;
    case 4:
        onCharacterRelive((PMSG_CharacterRelive*)(data + 2));
        break;
    case 5:
        onCharacterLevelUp((PMSG_PlayerLevelUp*)(data + 2));
        break;
    case 6:
        onCharacterIncAttributePoint((PMSG_IncAttributePoint*)(data + 2));
        break;
    case 7:
        onPoisoningBloodLoss(*(WORD*)(data + 2));
        break;
    case 8:
        sub_40AD00(data - 2);
        break;
    case 0x10:
        onCharacterItemData((PMSG_PlayerBagItems*)(data + 2));
        break;
    case 0x11:
        onCharacterSkillData((PMSG_PlayerSkillData*)(data + 2));
        break;
    case 0x13:
    {
        u_short gameId = ntohs(*(u_short*)(data + 2));
        int pos = gameObjId2Index(gameId);
        sub_419520(pos, data + 3);
        break;
    }
    case 0x14:
    {
        auto gridItem = (PMSG_GridItem*)(data + 2);
        sub_433E00(mainData.word_7CC6C78, 8, 8, gridItem->position, gridItem->itemType, gridItem->itemAttr1, gridItem->itemAttr2, 0);
        sub_401570(49, 0, 0);
        break;
    }
    case 0x20:
    {
        // 召唤兽的血量百分比，开始时100
        mainData.summonLife_5727424 = data[2];
        break;
    }
    case 0x22:
        mainData.dword_5727190 = data[2];
        break;
    case 0x23:
        sub_40BFA0(data - 2);
        break;
    default:
        assert(false);
        break;
    }
}

void ClientProtocol::handle_0xF4(BYTE* data)
{
    switch (data[1])
    {
    case 2:
        onServerList((PMSG_ServerList*)(data + 2));
        break;
    case 3:
        onServerAddr((PMSG_ServerAddr*)(data + 2));
        break;
    case 4:
    default:
        assert(false);
        break;
    }
}

void ClientProtocol::onPlayerMessage(PMSG_PlayerMessage* playerMessage)
{
    char name[12];
    lstrcpyn(name, playerMessage->name, 10);
    switch (playerMessage->message[0])
    {
    case '~':
        gamePrintLog(name, playerMessage->message + 1, 4);
        break;
    case '@':
        gamePrintLog(name, playerMessage->message + 1, 5);
        break;
    case '#':
        sub_428140(name, playerMessage->message + 1, 1);
        break;
    default:
        sub_428140(name, playerMessage->message, 0);
        gamePrintLog(name, playerMessage->message, 3);
        break;
    }
}

void ClientProtocol::onNpcMessage(PMSG_NpcMessage* npcMessage)
{
    auto gameObjId = ntohs(npcMessage->gameObjId);
    int v1 = gameObjId2Index(gameObjId);
    sub_427FA0((char*)mainData.newData3ACA0h_79DBE84 + 800 * v1 + 380, npcMessage->message, mainData.newData3ACA0h_79DBE84 + 800 * v1, 0, -1);
}

void ClientProtocol::onPlayerPrivateMessage(PMSG_PlayerMessage* playerMessage)
{
    char name[12];
    lstrcpyn(name, playerMessage->name, 10);
    gamePrintLog(name, playerMessage->message, 0);
}

void ClientProtocol::onSystemNotice(PMSG_SystemNotice* systemNotice)
{
    if (systemNotice->msgType) {
        switch (systemNotice->msgType)
        {
        case 1:
            gamePrintLog("", systemNotice->msg, 1);
            break;
        case 2:
        {
            char buf[100];
            sprintf(buf, mainData.textBmd_7B7BCE8[483], systemNotice->msg);
            sub_427620(buf, 1);
            break;
        }
        default:
            break;
        }
    }
    else {
        sub_427620(systemNotice->msg, 0);
    }
}

void ClientProtocol::onGameObjMove(PMSG_GameObjMove* gameObjMove)
{
    WORD gameObjId = ntohs(gameObjMove->gameObjId);
    BYTE* v2 = mainData.newData3ACA0h_79DBE84 + 800 * gameObjId2Index(gameObjId);
    if (!v2[649])
    {
        v2[648] = gameObjMove->dir >> 4;
        if (gameObjId == mainData.myGameObjId_57273C4)
        {
            if (!v2[631])
            {
                *(DWORD*)(v2 + 788) = gameObjMove->x;
                *(DWORD*)(v2 + 792) = gameObjMove->y;
            }
        }
        else
        {
            v2[658] = gameObjMove->x;
            v2[659] = gameObjMove->y;
            if (!v2[732])
            {
                if (sub_40E2D0(*(DWORD*)(v2 + 788), *(DWORD*)(v2 + 792), gameObjMove->x, gameObjMove->y, v2 + 736, 0.0))
                {
                    v2[631] = 1;
                }
                else
                {
                    v2[631] = 0;
                    sub_411690(v2);
                }
            }
        }
    }
}

void ClientProtocol::onMeetThePlayer(PMSG_MeetThePlayer* meetThePlayer)
{
    //DoCx_0B121C242A_405710(v3, v1);
    //DoCx_12_408B20((int)v3, v1);
    char name[12];
    for (size_t i = 0; i < meetThePlayer->count; i++)
    {
        auto& player = meetThePlayer->players[i];
        int gameObjId = ntohs(player.gameObjId);
        memcpy(name, player.name, 10);
        name[10] = 0;
        if (!stringSearch(name, "Webzen", false)) {
            BYTE* p = sub_419110(gameObjId & 0x7fff, 340, player.x1, player.y1, 0);
            p[376] = player.muClass >> 5;
            p[377] = (player.muClass >> 4) & 1;
            p[120] = 1;
            p[629] = player.dir & 0xf;
            switch (player.muClass & 0xf)
            {
            case 1:
                sub_41DDC0(p);
                //DoCx_0B121C242A_405710((const void*)a1, a2);
                break;
            case 2:
                if (p[376] == 2)
                    setAnimIndex(p, 110);
                else
                    setAnimIndex(p, 108);
                break;
            case 3:
                if (p[376] == 2)
                    setAnimIndex(p, 115);
                else
                    setAnimIndex(p, 114);
                break;
            case 4:
                if (p[376] == 2)
                    setAnimIndex(p, 113);
                else
                    setAnimIndex(p, 112);
                break;
            default:
                break;
            }

            p[113] |= (player.equipment.flags & 0xf);
            *(float*)(p + 32) = ((player.dir >> 4) - 1.0f) * 45.0f;
            *(DWORD*)(p + 788) = player.x1;
            *(DWORD*)(p + 792) = player.y1;
            p[658] = player.x1;
            p[659] = player.y1;

            if (gameObjId & 0x8000) {
                *(float*)(p + 12) = ((double) * (signed int*)(p + 788) + 0.5) * 100.0;
                *(float*)(p + 16) = ((double) * (signed int*)(p + 792) + 0.5) * 100.0;
                sub_41E430(1212, p + 12, p + 24, p + 216, 0, p, -1, 0);
                *(DWORD*)(p + 344) = 0;
            }
            else if (sub_40E2D0(
                *(DWORD*)(p + 788),
                *(DWORD*)(p + 792),
                player.x2,
                player.y2,
                p + 736,
                0.0))
            {
                p[631] = 1;
            }
            int index = gameObjId2Index(gameObjId & 0x7fff);
            sub_419520(index, &player.equipment);
            lstrcpyn((char*)p + 380, player.name, 11);
        }
    }
}

void ClientProtocol::onMeetTheMonster(PMSG_MeetTheMonster* meetTheMonster)
{
    for (size_t i = 0; i < meetTheMonster->count; i++)
    {
        auto& monster = meetTheMonster->monsters[i];
        WORD gameObjId = ntohs(monster.gameObjId);
        BYTE* p = addMonster(monster.typeIndex, monster.x1, monster.y1, gameObjId & 0x7fff);
        if (!p)
            break;
        p[113] |= (monster.flags & 0xf);
        *(float*)(p + 32) = ((monster.dir >> 4) - 1.0f) * 45.0f;
        *(DWORD*)(p + 788) = monster.x1;
        *(DWORD*)(p + 792) = monster.y1;
        p[658] = monster.x1;
        p[659] = monster.y1;
        if (gameObjId & 0x8000) {
            sub_409050(p);
        }
        else {
            bool f = sub_40E2D0(
                *(DWORD*)(p + 788),
                *(DWORD*)(p + 792),
                monster.x2,
                monster.y2,
                p + 736,
                0.0);
            if (f)
                p[631] = 1;
        }
    }
}

void ClientProtocol::onGameObjOutOfRange(PMSG_GameObjOutOfRange* gameObjOutOfRange)
{
    for (size_t i = 0; i < gameObjOutOfRange->count; i++)
    {
        auto gameObjId = ntohs(gameObjOutOfRange->gameObjIds[i]);
        sub_418B30(gameObjId & 0x7FFF);
    }
}

void ClientProtocol::onBloodLoss(PMSG_BloodLoss* bloodLoss)
{
    float color[3];

    auto gameObjId = ntohs(bloodLoss->gameObjId);
    auto loss = ntohs(bloodLoss->loss);
    auto objId = gameObjId & 0x7fff;
    int objIndex = gameObjId2Index(objId);
    BYTE* p = mainData.newData3ACA0h_79DBE84 + 800 * objIndex;
    if (!(gameObjId >> 15))
    {
        if (objId == mainData.myGameObjId_57273C4)
        {
            if (loss < mainData.myGameObjInfo_7B786CC->life)
                mainData.myGameObjInfo_7B786CC->life -= loss;
            else
                mainData.myGameObjInfo_7B786CC->life = 0;
            color[0] = 1.0f;
            color[1] = 0.0f;
        }
        else
        {
            unsigned int v9 = rand() & 0x80000001;
            bool v8 = v9 == 0;
            if ((v9 & 0x80000000) != 0)
                v8 = ((v9 - 1) | 0xFFFFFFFE) == -1;
            if (v8)
                gameObjBloodLoss(p, loss);
            color[0] = 1.0f;
            color[1] = 0.6f;
        }
        color[2] = 0.0f;
        sub_41BDB0(p + 12, loss, color);
        *(WORD*)(p + 644) = loss;
        return;
    }
    gameObjBloodLoss(p, loss);
    color[0] = 1.0f;
    color[1] = 0.0f;
    color[2] = 0.0f;
    sub_41BDB0(p + 12, loss, color);

    if (objId != mainData.myGameObjId_57273C4)
    {
        *(WORD*)(p + 644) = loss;
        return;
    }

    if (loss < mainData.myGameObjInfo_7B786CC->life)
    {
        mainData.myGameObjInfo_7B786CC->life -= loss;
        *(WORD*)(p + 644) = loss;
    }
    else
    {
        mainData.myGameObjInfo_7B786CC->life = 0;
        *(WORD*)(p + 644) = loss;
    }
}

void ClientProtocol::onKillMonster(PMSG_KillMonster* killMonster)
{
    BYTE* v5;

    auto gameObjId = ntohs(killMonster->monster);
    auto exp = ntohs(killMonster->exp);
    auto lossOfBlood = ntohs(killMonster->lossOfBlood);

    auto objIndex = gameObjId2Index(gameObjId & 0x7fff);
    float color[3] = { 1.0f, 0.6f, 0.0f };

    v5 = mainData.newData3ACA0h_79DBE84 + 800 * objIndex;

    if (gameObjId & 0xFFFF8000)
    {
        sub_412860(v5);
        sub_41BDB0(v5 + 12, lossOfBlood, color);
    }
    else
    {
        mainData.dword_79DBE88[640] = 2;
        *(WORD*)(mainData.dword_79DBE88 + 642) = lossOfBlood;
        *(WORD*)(mainData.dword_79DBE88 + 668) = objIndex;
        sub_41BDB0(v5 + 12, lossOfBlood, color);
    }
    v5[649] = 1;
    v5[631] = 0;

    mainData.myGameObjInfo_7B786CC->exp += exp;
    if (exp > 0)
    {
        char buf[100];
        sprintf(buf, mainData.textBmd_7B7BCE8[486], exp);
        gamePrintLog("", buf, 1);
    }
}

void ClientProtocol::onGameObjDeath(WORD gameObjId)
{
    gameObjId = ntohs(gameObjId);
    auto objIndex = gameObjId2Index(gameObjId);
    BYTE* p = mainData.newData3ACA0h_79DBE84 + 800 * objIndex;
    p[649] = 1;
    p[631] = 0;
    if (mainData.isDebug_81C4978)
    {
        char buf[100];
        sprintf(buf, "0x17 recv(%d)", gameObjId);
        gamePrintLog("", buf, 1);
    }
}

void ClientProtocol::onGameObjAction(PMSG_GameObjAction* gameObjAction)
{
    signed int objIndex;
    BYTE* v2;
    double rotateDir;

    auto gameObjId = ntohs(gameObjAction->gameObjId);
    objIndex = gameObjId2Index(gameObjId);
    v2 = mainData.newData3ACA0h_79DBE84 + 800 * objIndex;
    rotateDir = (gameObjAction->dir - 1.0) * 45.0;
    v2[631] = 0;
    *(float*)(v2 + 32) = rotateDir;
    *(float*)(v2 + 12) = v2[658] * 100.0 + 50.0;
    *(float*)(v2 + 16) = v2[659] * 100.0 + 50.0;
    bool isWoman = v2[376] == 2;
    switch (gameObjAction->action)
    {
    case 0x66:
    case 0x67:
        sub_411690(v2);
        break;
    case 0x64:
    case 0x65:
        sub_411E00(v2);
        v2[641] = 1;
        *(DWORD*)(v2 + 248) = 0;
        *(WORD*)(v2 + 668) = -1;
        mainData.dword_5727428 = objIndex;
        break;
    case 0x12:
        setAnimIndex(v2, 70);
        sub_401570(80, 0, 0);
        break;
    case 0x6C:
        if (isWoman)
            setAnimIndex(v2, 110);
        else
            setAnimIndex(v2, 108);
        break;
    case 0x6D:
        if (isWoman)
            setAnimIndex(v2, 115);
        else
            setAnimIndex(v2, 114);
        break;
    case 0x6E:
        if (isWoman)
            setAnimIndex(v2, 113);
        else
            setAnimIndex(v2, 112);
        break;
    case 0x6F:
        if (isWoman)
            setAnimIndex(v2, 72);
        else
            setAnimIndex(v2, 71);
        break;
    case 0x70:
        if (isWoman)
            setAnimIndex(v2, 74);
        else
            setAnimIndex(v2, 73);
        break;
    case 0x71:
        if (isWoman)
            setAnimIndex(v2, 76);
        else
            setAnimIndex(v2, 75);
        break;
    case 0x72:
        if (isWoman)
            setAnimIndex(v2, 82);
        else
            setAnimIndex(v2, 81);
        break;
    case 0x73:
        if (isWoman)
            setAnimIndex(v2, 80);
        else
            setAnimIndex(v2, 79);
        break;
    case 0x74:
        if (isWoman)
            setAnimIndex(v2, 84);
        else
            setAnimIndex(v2, 83);
        break;
    case 0x75:
        if (isWoman)
            setAnimIndex(v2, 86);
        else
            setAnimIndex(v2, 85);
        break;
    case 0x77:
        if (isWoman)
            setAnimIndex(v2, 88);
        else
            setAnimIndex(v2, 87);
        break;
    case 0x78:
        if (isWoman)
            setAnimIndex(v2, 90);
        else
            setAnimIndex(v2, 89);
        break;
    case 0x76:
        if (isWoman)
            setAnimIndex(v2, 78);
        else
            setAnimIndex(v2, 77);
        break;
    case 0x79:
        if (isWoman)
            setAnimIndex(v2, 92);
        else
            setAnimIndex(v2, 91);
        break;
    case 0x7A:
        if (isWoman)
            setAnimIndex(v2, 94);
        else
            setAnimIndex(v2, 93);
        break;
    case 0x7B:
        if (isWoman)
            setAnimIndex(v2, 96);
        else
            setAnimIndex(v2, 95);
        break;
    case 0x7C:
        if (isWoman)
            setAnimIndex(v2, 98);
        else
            setAnimIndex(v2, 97);
        break;
    case 0x7D:
        if (isWoman)
            setAnimIndex(v2, 100);
        else
            setAnimIndex(v2, 99);
        break;
    case 0x7E:
        setAnimIndex(v2, 101);
        break;
    case 0x7F:
        setAnimIndex(v2, 102);
        break;
    case 0x80:
        setAnimIndex(v2, 103);
        break;
    default:
        setAnimIndex(v2, gameObjAction->action);
        break;
    }

    if (mainData.isDebug_81C4978)
    {
        char buf[100];
        sprintf(buf, "0x18 recv(%x, %x)", gameObjAction->dir & 0xff, gameObjAction->action & 0xff);
        gamePrintLog("", buf, 1);
    }
}

void ClientProtocol::onUseSingleSkill(PMSG_UseSingleSkill* useSingleSkill)
{
    signed int userIndex;
    signed int targetIndex;

    BYTE* v6;
    BYTE* v7;
    BYTE skillId;
    char v9;

    auto userId = ntohs(useSingleSkill->user);
    auto targetId = ntohs(useSingleSkill->target);

    userIndex = gameObjId2Index(userId);
    targetIndex = gameObjId2Index(targetId & 0x7FFF);

    if (targetIndex != 300)
    {
        mainData.dword_5727428 = userIndex;
        v6 = mainData.newData3ACA0h_79DBE84 + 800 * userIndex;
        v7 = mainData.newData3ACA0h_79DBE84 + 800 * targetIndex;
        if (v6 != mainData.dword_79DBE88 && useSingleSkill->skillId != 6 && v7[336])
            * (float*)(v6 + 32) = sub_40D440(
                *(float*)(v6 + 12),
                *(float*)(v6 + 16),
                *(float*)(v7 + 12),
                *(float*)(v7 + 16));
        *(WORD*)(v6 + 668) = targetIndex;
        v6[653] = (targetId >> 15) != 0;
        skillId = useSingleSkill->skillId;
        v6[654] = skillId;
        switch (skillId)
        {
        case 0x1Au:
        case 0x1Bu:
        case 0x1Cu:
        case 0x1Eu:
        case 0x1Fu:
        case 0x20u:
        case 0x21u:
        case 0x22u:
        case 0x23u:
            sub_401570(80, 0, 0);
            if (v6 != mainData.dword_79DBE88)
            {
                sub_412630(v6);
                *(DWORD*)(v6 + 248) = 0;
            }
            goto LABEL_28;
        case 3u:
            sub_401570(60, 0, 0);
            v9 = v7[245];
            if (v9 == 114 || v9 == 115 || v9 == 108 || v9 == 110)
                sub_411690(v7);
            goto LABEL_14;
        case 1u:
        case 2u:
        case 4u:
        case 7u:
        case 0xBu:
        case 0x11u:
        LABEL_14:
            if (v6 != mainData.dword_79DBE88)
            {
                if (*(WORD*)(v6 + 2) == 340)
                {
                    sub_412630(v6);
                    *(DWORD*)(v6 + 248) = 0;
                }
                else
                {
                    sub_411E00(v6);
                    *(DWORD*)(v6 + 248) = 0;
                }
            }
            goto LABEL_28;
        case 6u:
            sub_41DD70(v6);
            break;
        case 0x13u:
            setAnimIndex(v6, skillId + 37);
            v6[641] = 1;
            sub_401570(81, 0, 0);
            break;
        case 0x14u:
            setAnimIndex(v6, skillId + 37);
            v6[641] = 1;
            sub_401570(82, 0, 0);
            break;
        case 0x15u:
            setAnimIndex(v6, skillId + 37);
            v6[641] = 1;
            sub_401570(83, 0, 0);
            break;
        case 0x16u:
            setAnimIndex(v6, skillId + 37);
            v6[641] = 1;
            sub_401570(84, 0, 0);
            break;
        case 0x17u:
            if (v6[655] % 2)
                setAnimIndex(v6, 41);
            else
                setAnimIndex(v6, skillId + 37);

            v6[641] = 1;
            v6[655]++;
            sub_401570(84, 0, 0);
            break;
        case 0x18u:
            sub_409BC0(v6);
            goto LABEL_28;
        case 0x32u:
        LABEL_28:
            v6[641] = 1;
            break;
        default:
            break;
        }

        if (mainData.isDebug_81C4978)
        {
            char buf[100];
            sprintf(buf, "0x19 recv(%d)", useSingleSkill->skillId);
            gamePrintLog("", buf, 1);
        }
    }
}

void ClientProtocol::onEnterTheGate(PMSG_EnterTheGate* enterTheGate)
{
    float x100;
    float y100;
    char buf[256];

    *(DWORD*)(mainData.dword_79DBE88 + 788) = enterTheGate->x;
    *(DWORD*)(mainData.dword_79DBE88 + 792) = enterTheGate->y;

    x100 = (enterTheGate->x + 0.5) * 100.0;
    y100 = (enterTheGate->y + 0.5) * 100.0;
    *(float*)(mainData.dword_79DBE88 + 12) = x100;
    *(float*)(mainData.dword_79DBE88 + 16) = y100;
    *(float*)(mainData.dword_79DBE88 + 20) = sub_43C880(x100, y100);
    *(float*)(mainData.dword_79DBE88 + 32) = (enterTheGate->dir - 1.0) * 45.0;
    if (enterTheGate->gateNumber)
    {
        sub_444B30();
        sub_418AD0(mainData.myGameObjId_57273C4);
        if (mainData.currMapNumber_81C1528 != enterTheGate->destMapNumber)
        {
            mainData.currMapNumber_81C1528 = enterTheGate->destMapNumber;
            LoadTerrain(enterTheGate->destMapNumber);
            *(float*)(mainData.dword_79DBE88 + 20) = sub_43C880(*(float*)(mainData.dword_79DBE88 + 12), *(float*)(mainData.dword_79DBE88 + 16));
            sprintf(buf, "%s%s", mainData.textBmd_7B7BCE8[30 + mainData.currMapNumber_81C1528], mainData.textBmd_7B7BCE8[484]);
        }
        SendF312_405E70();
        mainData.dword_7C41CAC = 30;
        mainData.dword_7C41CB8 = 0;
        dword_47CE38 = 6;
        mainData.byte_7CC88C0 = 0;
        mainData.byte_7CC88C1 = 0;
        mainData.btnItem_7CC88BF = 0;
        sub_41E430(1212, mainData.dword_79DBE88 + 12, mainData.dword_79DBE88 + 24, mainData.dword_79DBE88 + 216, 0, mainData.dword_79DBE88, -1, 0);
        *(DWORD*)(mainData.dword_79DBE88 + 344) = 0;
        mainData.byte_81C152C = 0;
    }
    else
    {
        sub_41DDC0(mainData.dword_79DBE88);
        mainData.byte_5727418 = 0;
    }
    mainData.dword_79DBE88[631] = 0;
    sub_411690(mainData.dword_79DBE88);

    if (mainData.isDebug_81C4978)
    {
        sprintf(buf, "0x1C recv(%d)", enterTheGate->gateNumber);
        gamePrintLog("", buf, 1);
    }
}

void ClientProtocol::onUseRangeSkill(PMSG_UseRangeSkill* useRangeSkill)
{
    BYTE* v1;
    bool v2;
    BYTE skillId;

    auto gameObjId = ntohs(useRangeSkill->user);
    int objIndex = gameObjId2Index(gameObjId);
    v1 = mainData.newData3ACA0h_79DBE84 + 800 * objIndex;
    v2 = *(WORD*)(v1 + 2) == 340;
    v1[654] = useRangeSkill->skillId;
    if (!v2)
        goto LABEL_15;
    if (v1 == mainData.dword_79DBE88)
        goto LABEL_11;
    skillId = useRangeSkill->skillId;
    if (skillId != 24)
    {
        if (skillId == 12)
        {
            setAnimIndex(v1, 67);
        }
        else if (skillId == 10)
        {
            setAnimIndex(v1, 68);
        }
        else
        {
            sub_412630(v1);
        }
    }
    else
    {
    LABEL_15:
        sub_411E00(v1);
    }
    *(DWORD*)(v1 + 248) = 0;
LABEL_11:
    v1[641] = 1;
    *(WORD*)(v1 + 668) = -1;
    v1[640] = 0;
    v1[660] = useRangeSkill->x;
    v1[661] = useRangeSkill->y;

    if (mainData.isDebug_81C4978)
    {
        char buf[100];
        sprintf(buf, "0x1E recv(%d)", useRangeSkill->skillId);
        gamePrintLog("", buf, 1);
    }
}

void ClientProtocol::onMeetTheSummon(PMSG_MeetTheSummon* meetTheSummon)
{
    char buf[100];
    for (size_t i = 0; i < meetTheSummon->count; i++)
    {
        auto& summon = meetTheSummon->summons[i];
        WORD gameObjId = ntohs(summon.gameObjId);
        BYTE* v3 = addMonster(summon.monsterId, summon.x1, summon.y1, gameObjId & 0x7FFF);
        if (!v3) break;

        v3[113] |= (summon.flags & 0xf);
        *(float*)(v3 + 32) = ((double)(unsigned __int8)(summon.dir >> 4) - 1.0) * 45.0;
        *(DWORD*)(v3 + 788) = summon.x1;
        *(DWORD*)(v3 + 792) = summon.y1;
        v3[658] = summon.x1;
        v3[659] = summon.y1;
        v3[120] = 1;
        BYTE v5 = summon.dir & 0xF;
        v3[629] = v5;
        if (v5 >= 6u)
            * (WORD*)(v3 + 378) = 1;
        // 重命名为xx的召唤兽名
        strcpy(buf, (const char*)(v3 + 380));
        lstrcpyn((char*)v3 + 380, summon.ownerName, 10);
        strcat((char*)v3 + 380, mainData.textBmd_7B7BCE8[485]);
        strcat((char*)v3 + 380, buf);

        if (gameObjId >> 15)
        {
            sub_409050(v3);
            sub_41E430(233, v3 + 12, v3 + 24, v3 + 216, 0, v3, -1, 0);
            sub_4212F0(1141, (char*)v3 + 12, (char*)v3 + 24, (char*)v3 + 216, 2, 1.0, (char*)v3);
        }
        else if (sub_40E2D0(*(DWORD*)(v3 + 788), *(DWORD*)(v3 + 792), summon.x2, summon.y2, v3 + 736, 0.0))
        {
            v3[631] = 1;
        }
    }
    if (mainData.isDebug_81C4978)
    {
        sprintf(buf, "0x1F recv(%d)", meetTheSummon->count);
        gamePrintLog("", buf, 1);
    }
}

void ClientProtocol::onItemsOnGround(PMSG_ItemsOnGround* itemsOnGround)
{
    //printBinary(itemsOnGround, *((BYTE*)itemsOnGround - 2) - 4);

    auto item = itemsOnGround->items;
    int itemNumber;
    int drop;
    float pos[2];

    for (size_t i = 0; i < itemsOnGround->count; i++)
    {
        itemNumber = ntohs(item->itemNumber);
        drop = itemNumber >> 15;
        itemNumber &= 0x7fff;
        if (itemNumber > 1000)
            itemNumber = 0;

        pos[0] = (item->x + 0.5) * 100.0;
        pos[1] = (item->y + 0.5) * 100.0;

        BYTE* p = mainData.byte_7C41DC8 + 448 * itemNumber;
        if (item->itemType == 0xef) {
            // 金
            int money = item->moneyOrAttr[0] << 16 | item->moneyOrAttr[1] << 8 | item->moneyOrAttr[2];
            sub_444F70(p, 589, money, 0, pos, drop);
            if (drop) {
                sub_401570(31, nullptr, 0);
            }
            item = (PMSG_ItemsOnGround::Item*)((BYTE*)item + 8);
        }
        else {
            // 物品
            sub_444F70(p, item->itemType + 350, item->moneyOrAttr[0], item->moneyOrAttr[2], pos, drop);
            if (drop)
            {
                if (item->itemType == 0xed ||   // 祝福宝石
                    item->itemType == 0xee ||   // 灵魂宝石
                    item->itemType == 0xcf)     // 玛雅宝石
                {
                    sub_401570(49, p + 72, 0);
                }
                else
                {
                    sub_401570(30, p + 72, 0);
                }
            }
            item = (PMSG_ItemsOnGround::Item*)((BYTE*)item + 7);
        }
    }
}

void ClientProtocol::onItemsLostOnGround(PMSG_ItemsLostOnGround* itemsLostOnGround)
{
    for (size_t i = 0; i < itemsLostOnGround->count; i++)
    {
        int itemNumber = ntohs(itemsLostOnGround->itemNumbers[i]);
        if (itemNumber < 0 || itemNumber>1000)
            itemNumber = 0;
        BYTE* p = mainData.byte_7C41DC8 + 448 * itemNumber;
        p[72] = 0;
    }
}

void ClientProtocol::onAddItemToBag(PMSG_GridItem* gridItem)
{
    int money = 0;
    if (gridItem->position != 0xFFu)
    {
        auto itemType = gridItem->itemType;
        if (gridItem->position == 0xFEu) {
            money = ntohl(*(u_long*)& gridItem->itemType);
            *((DWORD*)mainData.newData570h_7B786D4 + 334) = money;
        }
        else
            sub_433E00(mainData.word_7CC6C78, 8, 8, gridItem->position, gridItem->itemType, gridItem->itemAttr1, gridItem->itemAttr2, 0);

        sub_401570((itemType == 0xEDu || itemType == 0xEEu) ? 49 : 29, mainData.dword_79DBE88, 0);
    }

    dword_47C66C = -1;
    if (mainData.isDebug_81C4978)
    {
        char buf[100];
        sprintf(buf, "0x22 recv(%d %x)", gridItem->position, money);
        gamePrintLog("", buf, 1);
    }
}

void ClientProtocol::onDiscardItem(PMSG_DiscardItem* discardItem)
{
    if (discardItem->v1)
    {
        int pos = discardItem->position;
        if (pos >= 0xC)
        {
            sub_4340D0(pos, mainData.word_7CC6C78, 8);
        }
        else
        {
            BYTE* v1 = mainData.newData570h_7B786D4 + 68 * pos + 520;
            *(WORD*)v1 = -1;
            *((DWORD*)v1 + 14) = 0;
            sub_419240(mainData.dword_79DBE88);

            if (pos == 8)
            {
                sub_442AD0(mainData.dword_79DBE88);
            }
        }
        mainData.dword_7CAFFB8 = 0;
    }
    else
    {
        sub_4342D0();
    }
    dword_47C670 = -1;
}

void ClientProtocol::onMoveItem(PMSG_MoveItem* moveItem)
{

    BYTE* v2;
    int v3;

    mainData.byte_7CC88E1[0] = 0;
    if (moveItem->v1 == 0xFF)
    {
        sub_4342D0();
    }
    else
    {
        mainData.dword_7CAFFB8 = 0;
        switch (moveItem->v1)
        {
        case 0:
            v3 = 8;
            v2 = mainData.word_7CC6C78;
            goto LABEL_7;
        case 1:
            v3 = 4;
            v2 = mainData.word_7CC63F0;
            goto LABEL_7;
        case 2:
            v3 = 15;
            v2 = mainData.byte_7CC4398;
            goto LABEL_7;
        case 3:
            v3 = 4;
            v2 = mainData.byte_7CC8000;
        LABEL_7:
            sub_433E00(v2, 8, v3, moveItem->gridItem.position, moveItem->gridItem.itemType, moveItem->gridItem.itemAttr1, moveItem->gridItem.itemAttr2, 0);
            break;
        default:
            break;
        }
        sub_401570(29, 0, 0);
    }

    if (mainData.isDebug_81C4978)
    {
        char buf[100];
        sprintf(buf, "0x24 recv(%d %d %d %d %d)", moveItem->v1, moveItem->gridItem.position, moveItem->gridItem.itemType, moveItem->gridItem.itemAttr1, moveItem->gridItem.itemAttr2);
        gamePrintLog("", buf, 1);
    }
}

void ClientProtocol::onLifeChange(PMSG_LifeManaChange* lifeChange)
{
    BYTE* p;
    if (lifeChange->flags) {
        switch (lifeChange->flags)
        {
        case 0xFF:
            mainData.myGameObjInfo_7B786CC->life = ntohs(lifeChange->number);
            break;
        case 0xFE:
            mainData.myGameObjInfo_7B786CC->maxLife = ntohs(lifeChange->number);
            break;
        case 0xFD:
            mainData.dword_5727420 = 0;
            break;
        default:
            p = mainData.byte_7CC6C92 + (lifeChange->flags - 12) * 68;
            if (*p)
                (*p)--;
            if (!*p) {
                *(WORD*)(p - 26) = 0xFFFF;
                *(DWORD*)(p + 30) = 0;
            }
            break;
        }
    }
    if (mainData.isDebug_81C4978) {
        char buf[100];
        sprintf(buf, "0x26 recv(%d)", lifeChange->flags);
        gamePrintLog("", buf, 1);
    }
}

void ClientProtocol::onManaChange(PMSG_LifeManaChange* manaChange)
{
    mainData.myGameObjInfo_7B786CC->mana = ntohs(manaChange->number);
    if (manaChange->flags != 0xFF) {
        BYTE* p = mainData.byte_7CC6C92 + (manaChange->flags - 12) * 68;
        if (*p)
            (*p)--;
        if (!*p) {
            *(WORD*)(p - 26) = 0xFFFF;
            *(DWORD*)(p + 30) = 0;
        }
    }
}

void ClientProtocol::onUsePotion(PMSG_UsePotion* usePotion)
{
    if (usePotion->flags >= 0xC) {
        BYTE* p1 = mainData.byte_7CC6C92 + (usePotion->flags - 12) * 68;
        WORD v5 = *(WORD*)(p1 - 26);
        BYTE* v7 = mainData.newData3700h1_7BA8518 + 55 * v5;
        for (size_t i = 0; i < v7[33]; i++)
        {
            for (size_t j = 0; j < v7[32]; j++)
            {
                int v9 = 17 * (8 * i + usePotion->flags + j - 12);
                *(BYTE*)(mainData.byte_7CC6C92 + 4 * v9) = usePotion->number;
            }
        }
    }
    else {
        *((BYTE*)mainData.newData570h_7B786D4 + 68 * usePotion->flags + 546) = usePotion->number;
    }
    if (usePotion->v2)
        mainData.dword_5727420 = 0;
}

void ClientProtocol::onOpenBag(BYTE n)
{
    mainData.btnItem_7CC88BF = 1;
    if (n == 2)
    {
        // 仓库
        mainData.byte_7CC88C1 = 1;
    }
    else if (n == 3)
    {
        // 玛雅哥布林
        mainData.byte_7CC88C2 = 1;
        mainData.dword_7CC88C8 = 0;
    }
    else
    {
        // 商店
        mainData.byte_7CC88C0 = 1;
        mainData.byte_7CC88D0 = 0;
    }
    mainData.btnCharacter_7CC88BE = 0;
    mainData.btnGuild_7CC88BC = 0;
    mainData.btnTeam_7CC88BD = 0;
    sub_401570(25, 0, 0);
    sub_401570(28, 0, 0);
    SetCursorPos(g_wndWidth * (260 * mainData.curMouseXrate_81C17B4 / 640) / 640, mainData.curMouseYrate_81C17B0 * g_wndHeight / 480);
}

void ClientProtocol::onShopItems(PMSG_ShopItems* shopItems)
{
    if (shopItems->flags == 3)
    {
        mainData.dword_7CC88C8 = 1;
        gamePrintLog("", mainData.textBmd_7B7BCE8[594], 2);  // 玛雅物品组合失败。
        sub_401570(67, 0, 0);
        sub_401570(48, 0, 0);

        BYTE* v1 = mainData.byte_7CC8038;
        for (size_t i = 0; i < 32; i++)
        {
            *(WORD*)(v1 - 56) = -1;
            *(DWORD*)v1 = 0;
            v1 += 68;
        }
    }
    else
    {
        BYTE* v2 = mainData.dword_7CC43D0;
        for (size_t i = 0; i < 120; i++)
        {
            *(WORD*)(v2 - 56) = -1;
            *(DWORD*)v2 = 0;
            v2 += 68;
        }
    }
    char buf[100];
    for (size_t i = 0; i < shopItems->count; i++)
    {
        auto& item = shopItems->gridItems[i];
        if (shopItems->flags == 3)
            sub_433E00(mainData.byte_7CC8000, 8, 4, item.position, item.itemType, item.itemAttr1, item.itemAttr2, 1);
        else
            sub_433D10(item.position, item.itemType, item.itemAttr1, item.itemAttr2);

        if (mainData.isDebug_81C4978)
        {
            sprintf(buf, "0x31 recv(%d)", item.itemType);
            gamePrintLog("", buf, 1);
        }
    }
}

void ClientProtocol::onSellItem(PMSG_SellItem* sellItem)
{
    if (sellItem->flags)
    {
        mainData.dword_7CAFFB8 = 0;
        *((DWORD*)mainData.newData570h_7B786D4 + 334) = sellItem->money;
        sub_401570(29, 0, 0);
    }
    else
    {
        sub_4342D0();
    }
}

void ClientProtocol::onTeamInfo(PMSG_TeamInfo* teamInfo)
{
    mainData.dword_7CC889C = teamInfo->playerCount;

    for (size_t i = 0; i < teamInfo->playerCount; i++)
    {
        auto& info = mainData.teamPlayerInfos_7C41D38[i];
        auto& player = teamInfo->players[i];

        lstrcpyn(info.name, player.name, 10);
        memcpy(&info.v1, &player.v1, 4);
    }
}

void ClientProtocol::onGuildInfo(PMSG_GuildInfo* guildInfo)
{
    mainData.dword_7CC888C = guildInfo->playerCount;
    mainData.dword_7CC8890 = guildInfo->score;
    for (size_t i = 0; i < guildInfo->playerCount; i++)
    {
        auto& info = mainData.guildPlayerInfos_7CB0048[i];
        auto& player = guildInfo->players[i];

        lstrcpyn(info.name, player.name, 10);
        info.v1 = player.v1;
    }
}

void ClientProtocol::onServerList(PMSG_ServerList* serverList)
{
    for (size_t i = 0; i < 25; i++)
    {
        serverListInfos_81C1840[i].lineCount = 0;
    }
    for (size_t i = 0; i < serverList->count; i++)
    {
        int serverId = serverList->servers[i].code / 20;
        auto& serverListInfo = serverListInfos_81C1840[serverId];
        BYTE& lineCount = serverListInfo.lineCount;
        lstrcpyn(serverListInfo.name, serverId >= 12 ? mainData.textBmd_7B7BCE8[559] : mainData.textBmd_7B7BCE8[540 + serverId], sizeof(serverListInfo.name));
        serverListInfo.serverLineInfos[lineCount].code = serverList->servers[i].code;
        serverListInfo.serverLineInfos[lineCount].rate = serverList->servers[i].rate;
        lineCount++;
    }
}

void ClientProtocol::onServerAddr(PMSG_ServerAddr* serverAddr)
{
    auto net = GameNet::getInstance();
    net->close();
    net->connect(serverAddr->ip, serverAddr->port);
    char buf[200];
    sprintf(buf, "%s %s:%d", mainData.textBmd_7B7BCE8[481], serverAddr->ip, serverAddr->port);
    gamePrintLog("", buf, 1);
}

void ClientProtocol::onConnectedGameServer(PMSG_GameObjIdAndVersion* objIdAndVersion)
{
    if (objIdAndVersion->f == 1)
    {
        mainData.myGameObjId_57273C4 = ntohs(objIdAndVersion->gameObjId);
        mainData.dword_57273C8 = 2;
        mainData.dword_81C497C = 2;
        sub_401570(27, 0, 0);
    }
    else
    {
        mainData.dword_81C498C = 113;
    }
    if (strncmp(objIdAndVersion->version, g_gameVersion, 3) != 0)
        mainData.dword_81C498C = 112;

    for (size_t i = 0; i < 1000; i++)
    {
        mainData.dword_7CB0268[i].v1 = -1;
    }
}

/*
登录验证结果
0密码错误
1通过
2帐号错误
3该帐号正在使用中
4本服务器容纳的人数已满
5这是目前被禁止使用的帐号
6您的游戏版本不对请到官方网站下载最新的版本
7连接发生错误
8失败3次连接中断
9连接发生错误
10本帐号的使用期限已到
11本帐号的储值点数不足
12这个IP的使用期限已到期
*/
void ClientProtocol::onLoginResult(int state)
{
    if (state == 1) {
        mainData.dword_57273C8 = 20;
        mainData.dword_57273FC = 2;
        sub_403240();
        mainData.dword_81C497C = 3;
    }
    else if (state >= 0 && state < 14) {
        int nums[14] = { 21, 20, 22, 23, 24, 25, 26, 27, 28, 27, 29, 30, 31, 32 };
        mainData.dword_57273C8 = nums[state];
        mainData.dword_81C497C = 3;
    }
}

void ClientProtocol::onSceneSelected(int n)
{
    mainData.byte_5727408 = 0;
    switch (n)
    {
    case 0:
        SendMessageA(mainData.hWnd1_54CA690, WM_DESTROY, 0, 0);
        break;
    case 1:
    case 2:
        if (nScene_482904 == 5)
        {
            sub_44FD20();
            sub_4342D0();
            sub_44DD00();
        }
        if (n == 1) {
            nScene_482904 = 4;
            Send_F3_00_405CD0();
        }
        else {
            GameNet::getInstance()->close();
            sub_44D3B0();
            nScene_482904 = 2;
            mainData.byte_81C49A1 = 0;
        }
        mainData.byte_81C49A3 = 0;
        mainData.byte_81C49A4 = 0;
        mainData.byte_81C4979 = 0;
        mainData.dword_57273C8 = 0;
        sub_407960();
        break;
    default:
        break;
    }

    if (mainData.isDebug_81C4978)
    {
        char buf[100];
        sprintf(buf, "0xF1 02 recv(%d)", n);
        gamePrintLog("", buf, 1);
    }
}

void ClientProtocol::onCharacterList(PMSG_CharacterList* characterList)
{
    int x, y, z;
    BYTE* data;

    for (size_t i = 0; i < characterList->count; i++)
    {
        auto& character = characterList->chars[i];
        z = (character.position - 1) * 15;
        y = character.position * 50 - 50;
        x = character.position * 100;
        data = sub_41B810(character.position, character.muClass >> 5, (character.muClass >> 4) & 1, x, y, z);
        *(WORD*)(data + 378) = character.level;
        lstrcpyn((char*)(data + 380), character.name, 11);
        sub_419520(character.position, &character.equipment);
    }
    mainData.dword_57273C8 = 38;
    return;
}

void ClientProtocol::onCreateCharacter(PMSG_CreateCharacter_result* createCharacter_result)
{
    float x, y;
    BYTE* p1;

    switch (createCharacter_result->result)
    {
    case 0:
        mainData.dword_57273C8 = 42;
        break;
    case 1:
        y = createCharacter_result->position * 50.0 - 50.0;
        x = createCharacter_result->position * 100.0;
        p1 = sub_41B810(createCharacter_result->position, LOBYTE(mainData.dword_79DBCD0), HIBYTE(mainData.dword_79DBCD0), x, y, 0);
        *(WORD*)(p1 + 378) = 1;
        lstrcpyn((char*)p1 + 380, createCharacter_result->name, 10);
        mainData.dword_57273C8 = 41;
        break;
    case 2:
        mainData.dword_57273C8 = 43;
        break;
    default:
        break;
    }
}

void ClientProtocol::onCharacterDel(int state)
{
    mainData.dword_57273C8 = (state == 1) ? 45 : 46;
}

void ClientProtocol::onCharacterInfo(PMSG_CharacterInfo* charInfo)
{
    mainData.myGameObjInfo_7B786CC->exp = charInfo->exp;
    mainData.myGameObjInfo_7B786CC->expUp = charInfo->expUp;
    mainData.myGameObjInfo_7B786CC->levelUpPoint = charInfo->lvUpPoint;
    mainData.myGameObjInfo_7B786CC->strength = charInfo->str;
    mainData.myGameObjInfo_7B786CC->dexterity = charInfo->dex;
    mainData.myGameObjInfo_7B786CC->vitality = charInfo->vit;
    mainData.myGameObjInfo_7B786CC->energy = charInfo->ene;
    mainData.myGameObjInfo_7B786CC->life = charInfo->life;
    mainData.myGameObjInfo_7B786CC->maxLife = charInfo->maxlife;
    mainData.myGameObjInfo_7B786CC->mana = charInfo->mana;
    mainData.myGameObjInfo_7B786CC->maxMana = charInfo->maxMana;
    *((DWORD*)mainData.newData570h_7B786D4 + 334) = charInfo->money;	//钱
    mainData.currMapNumber_81C1528 = charInfo->mapNumber;
    LoadTerrain(mainData.currMapNumber_81C1528);

    mainData.dword_57273AC = rand() % 300;
    BYTE* v5 = mainData.newData3ACA0h_79DBE84 + 800 * mainData.dword_57273AC;
    sub_418BD0(v5, 340, charInfo->x, charInfo->y, (charInfo->x1 - 1.0) * 45.0);

    *(WORD*)(v5 + 406) = mainData.myGameObjId_57273C4;
    *(BYTE*)(v5 + 376) = mainData.myGameObjInfo_7B786CC->muClass1;
    *(BYTE*)(v5 + 377) = mainData.myGameObjInfo_7B786CC->muClass2;
    *(BYTE*)(v5 + 629) = charInfo->pkLv;
    *(BYTE*)(v5 + 120) = 1;
    sub_419240(v5);

    mainData.dword_79DBE88 = (BYTE*)v5;
    lstrcpyn((char*)v5 + 380, mainData.myGameObjInfo_7B786CC->name, 11);

    sub_41E430(1212, v5 + 12, v5 + 24, v5 + 216, 0, v5, -1, 0);
    mainData.dword_57273C8 = 51;
}

void ClientProtocol::onCharacterRelive(PMSG_CharacterRelive* characterRelive)
{
    //sub_407C30(data - 4);
    BYTE* v6;

    mainData.byte_5727418 = 0;
    mainData.dword_79DBE88[0] = 0;
    mainData.myGameObjInfo_7B786CC->life = characterRelive->life;
    mainData.myGameObjInfo_7B786CC->mana = characterRelive->mana;
    mainData.myGameObjInfo_7B786CC->exp = characterRelive->exp;
    *((DWORD*)mainData.newData570h_7B786D4 + 334) = characterRelive->money;
    for (size_t i = 0; i < 300; i++)
    {
        *(char*)(mainData.newData3ACA0h_79DBE84 + 800 * i) = 0;
    }
    float dir = (characterRelive->dir - 1.0f) * 45.0f;
    sub_418BD0(mainData.newData3ACA0h_79DBE84 + 800 * mainData.dword_57273AC, 340, characterRelive->x, characterRelive->y, dir);

    v6 = mainData.newData3ACA0h_79DBE84 + 800 * mainData.dword_57273AC;
    *(WORD*)(v6 + 406) = mainData.myGameObjId_57273C4;
    v6[376] = mainData.myGameObjInfo_7B786CC->muClass1;
    v6[377] = mainData.myGameObjInfo_7B786CC->muClass2;

    v6[629] = mainData.dword_79DBE88[629];
    v6[120] = 1;
    *(WORD*)(v6 + 404) = *(WORD*)(mainData.dword_79DBE88 + 404);
    v6[730] = 1;
    sub_419240(v6);
    sub_411690(v6);
    sub_41E430(1212, v6 + 12, v6 + 24, v6 + 216, 0, v6, -1, 0);
    if (mainData.byte_7CC88C0)
    {
        mainData.btnItem_7CC88BF = 0;
        sub_433BE0();
    }
    sub_444B30();
    sub_418AD0(mainData.myGameObjId_57273C4);
    if (mainData.currMapNumber_81C1528 != characterRelive->mapIndex)
    {
        mainData.currMapNumber_81C1528 = characterRelive->mapIndex;
        LoadTerrain(characterRelive->mapIndex);
        *(float*)(v6 + 20) = sub_43C880(*(float*)(v6 + 12), *(float*)(v6 + 16));
    }
    mainData.summonLife_5727424 = 0;
}

void ClientProtocol::onCharacterLevelUp(PMSG_PlayerLevelUp* playerLevelUp)
{
    mainData.myGameObjInfo_7B786CC->level = playerLevelUp->level;
    mainData.myGameObjInfo_7B786CC->levelUpPoint = playerLevelUp->levelUpPoint;
    mainData.myGameObjInfo_7B786CC->life = mainData.myGameObjInfo_7B786CC->maxLife = playerLevelUp->maxLife;
    mainData.myGameObjInfo_7B786CC->mana = mainData.myGameObjInfo_7B786CC->maxMana = playerLevelUp->maxMana;

    short n = *(short*)(mainData.newData570h_7B786D4 + 14);
    int m = (n + 9) * n * n * 10;
    *(DWORD*)(mainData.newData570h_7B786D4 + 36) = m;

    sub_41E430(
        1211,
        mainData.dword_79DBE88 + 12,
        mainData.dword_79DBE88 + 24,
        mainData.dword_79DBE88 + 216,
        0,
        mainData.dword_79DBE88,
        -1,
        0);
}

void ClientProtocol::onCharacterIncAttributePoint(PMSG_IncAttributePoint* incAttributePoint)
{
    switch (incAttributePoint->attr)
    {
    case 0:
        ++mainData.myGameObjInfo_7B786CC->strength;
        break;
    case 1:
        ++mainData.myGameObjInfo_7B786CC->dexterity;
        break;
    case 2:
        ++mainData.myGameObjInfo_7B786CC->vitality;
        mainData.myGameObjInfo_7B786CC->maxLife = incAttributePoint->n;
        break;
    case 3:
        ++mainData.myGameObjInfo_7B786CC->energy;
        mainData.myGameObjInfo_7B786CC->maxMana = incAttributePoint->n;
        break;
    default:
        assert(false);
        break;
    }
    std_sub_426DB0();
}

void ClientProtocol::onPoisoningBloodLoss(WORD number)
{
    number = ntohs(number);
    if (mainData.myGameObjInfo_7B786CC->life < number)
        mainData.myGameObjInfo_7B786CC->life = 0;
    else
        mainData.myGameObjInfo_7B786CC->life -= number;

    char buf[100];
    sprintf(buf, "中毒掉血 %d", number);
    gamePrintLog("", buf, 2);
}

void ClientProtocol::onCharacterItemData(PMSG_PlayerBagItems* playerBagItems)
{
    int v1 = 0;
    do
    {
        v1 += 68;
        *(WORD*)((char*)mainData.newData570h_7B786D4 + v1 + 452) = -1;
        *(DWORD*)((char*)mainData.newData570h_7B786D4 + v1 + 508) = 0;
    } while (v1 < 816);

    auto p = mainData.dword_7CC6CB0;
    for (size_t i = 0; i < 64; i++)
    {
        *(WORD*)((char*)p - 56) = -1;
        *(DWORD*)p = 0;
    }

    sub_442AD0(mainData.dword_79DBE88);

    for (size_t i = 0; i < playerBagItems->count; i++)
    {
        auto& item = playerBagItems->gridItems[i];
        sub_433E00(mainData.word_7CC6C78, 8, 8, item.position, item.itemType, item.itemAttr1, item.itemAttr2, 1);
    }

    if (mainData.isDebug_81C4978)
        gamePrintLog("", "0x10收到", 4);
}

void ClientProtocol::onCharacterSkillData(PMSG_PlayerSkillData* playerSkillData)
{
    if (playerSkillData->count) {
        for (size_t i = 0; i < playerSkillData->count; i++)
        {
            auto& skill = playerSkillData->skills[i];
            mainData.myGameObjInfo_7B786CC->skills[skill.pos] = skill.code;
        }
    }
    else {
        mainData.myGameObjInfo_7B786CC->skills[playerSkillData->skills->pos] = 0;
    }

    mainData.myGameObjInfo_7B786CC->skillCount = 0;
    for (size_t i = 0; i < sizeof(MyGameObjInfo::skills) / sizeof(MyGameObjInfo::skills[0]); i++)
    {
        if (mainData.myGameObjInfo_7B786CC->skills[i])
            mainData.myGameObjInfo_7B786CC->skillCount++;
    }

    BYTE* p = *(BYTE * *)mainData.dword_79DBE88;
    BYTE& index = p[797];
    if (index >= mainData.myGameObjInfo_7B786CC->skillCount ||
        mainData.myGameObjInfo_7B786CC->skillCount == 1 ||
        mainData.myGameObjInfo_7B786CC->skills[index] == 0) {
        index = 0;
    }
}
